/*
 * geoid.c -- ECEF to WGS84 conversions, including ellipsoid-to-MSL height
 *            and magnetic variation.
 *
 * Geoid separation code by Oleg Gusev, from data by Peter Dana.
 * ECEF conversion by Rob Janssen.
 *
 * This file is Copyright 2010 by the GPSD project
 * SPDX-License-Identifier: BSD-2-clause
 */

// #include "../include/gpsd_config.h"  /* must be before all includes */

#include <math.h>
#include "../include/gpsd.h"
// #include "../include/os_compat.h"

#ifdef __UNUSED
static double fix_minuz(double d);
static double atan2z(double y, double x);
#endif /* __UNUSED */

#define GEOID_ROW       37
#define GEOID_COL       73

/* This table is EGM2008.  Values obtained from GeoidEval, part of
 * geographiclib., by using devtools/get_geoid_table.py
 *
 * geoid_delta[][] has the geoid separation, in cm, on a 5 degree by 5
 * degree grid for the entire planet.
 */
/* *INDENT-OFF* */
const short geoid_delta[GEOID_ROW][GEOID_COL] = {
    /* -90 */
    { -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015,
      -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015,
      -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015,
      -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015,
      -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015,
      -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015,
      -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015, -3015,
      -3015, -3015, -3015},
    /* -85 */
    { -3568, -3608, -3739, -3904, -4039, -4079, -4033, -3946, -3845, -3734,
      -3603, -3458, -3310, -3163, -2994, -2827, -2695, -2667, -2737, -2823,
      -2840, -2757, -2634, -2567, -2547, -2540, -2452, -2247, -1969, -1704,
      -1540, -1507, -1552, -1592, -1573, -1513, -1465, -1478, -1542, -1577,
      -1483, -1256, -1029,  -957, -1066, -1216, -1262, -1194, -1118, -1129,
      -1231, -1370, -1504, -1641, -1813, -2028, -2255, -2455, -2630, -2811,
      -3022, -3242, -3436, -3578, -3658, -3676, -3640, -3578, -3527, -3490,
      -3532, -3570, -3568},
    /* -80 */
    { -5232, -5276, -5275, -5301, -5286, -5276, -5218, -5001, -4775, -4580,
      -4319, -4064, -3854, -3691, -3523, -3205, -2910, -2608, -2337, -2355,
      -2417, -2445, -2471, -2350, -2230, -2136, -1869, -1689, -1732, -1748,
      -1540, -1236, -1048,  -794,  -569,  -603,  -501,  -305,  -166,    19,
        146,   274,   444,   510,   534,   550,   458,   373,   473,   575,
        607,   732,   562,   153,  -271,  -825, -1300, -1861, -2475, -2866,
      -3434, -4001, -4196, -4533, -4989, -5152, -5094, -4983, -4987, -5065,
      -5055, -5115, -5232},
    /* -75 */
    { -6155, -6339, -6266, -6344, -6282, -6100, -6009, -5492, -5088, -4547,
      -4187, -3901, -3586, -3234, -3051, -2886, -2577, -2289, -1981, -1655,
      -1435, -1096,  -557,  -617,  -998,  -961,  -655,  -464,  -170,    79,
       -103,   -64,   150,   223,   819,  1006,  1174,  1136,  1211,  1278,
       1467,  1686,  1783,  1706,  1833,  1721,  1653,  1580,  1267,   953,
        629,   807,   774,   607,   217,  -386,  -814, -1354, -2452, -3542,
      -3833, -3932, -4259, -4962, -4977, -5536, -5753, -5800, -6012, -5835,
      -5751, -5820, -6155},
    /* -70 */
    { -6218, -6432, -6333, -6150, -6021, -5948, -5705, -5480, -5213, -4789,
      -4365, -4003, -3757, -3514, -3250, -3000, -2672, -2541, -2138, -1220,
       -844,  -277,   249,   906,   458,    69,    26,    98,   166,   130,
        118,   253,   303,   437,  1010,  1341,  1423,  1558,  1682,  1825,
       1766,  1917,  2027,  2047,  2164,  2909,  2882,  2997,  3010,  2687,
       1749,  1703,  1799,  1438,  1099,   346,  -813, -1432, -2149, -2320,
      -2704, -3085, -3907, -4172, -4287, -4846, -5466, -5592, -5576, -5525,
      -5800, -5954, -6218},
    /* -65 */
    { -5152, -5115, -5049, -4943, -4858, -4714, -4580, -4369, -4202, -4060,
      -3806, -3454, -3210, -3007, -2749, -2484, -2264, -1928, -1501, -1113,
       -614,    31,   642,  1502,  1833,  1844,  1268,  1442,  1441,  1302,
       1164,  1041,   945,   874,   896,  1059,  1368,  1680,  1736,  1975,
       1891,  1979,  2131,  2338,  2672,  2861,  3114,  3097,  2801,  2695,
       2422,  2022,  1648,  1340,   713,   352,  -127,  -895, -1740, -2040,
      -2854, -3292, -3453, -3922, -4395, -4538, -4554, -4356, -4445, -4669,
      -4988, -5122, -5152},
    /* -60 */
    { -4598, -4449, -4278, -4056, -3732, -3417, -3205, -3094, -3008, -2876,
      -2669, -2478, -2350, -2272, -2218, -1969, -1660, -1381, -1123,  -716,
       -350,   247,   924,  1712,  2016,  2066,  2032,  1556,  2123,  2322,
       2384,  1034,  2121,  1923,  1720,  1571,  1517,  1668,  2008,  2366,
       2546,  2736,  2914,  3169,  3395,  3467,  3315,  3286,  3279,  3073,
       2930,  2727,  2502,  1783,   893,   311,  -328,  -778, -1364, -1973,
      -2467, -2833, -3143, -3283, -3311, -3120, -2956, -3027, -3485, -3972,
      -4454, -4679, -4598},
    /* -55 */
    { -3414, -3429, -3223, -3013, -2704, -2474, -2292, -2185, -1962, -1818,
      -1828, -1485, -1259, -1284, -1327, -1304, -1097, -1071,  -628,  -326,
        174,   340,  1331,  1217,  1712,  1441,  1467,  1578,  1654,  2179,
        764,  1486,  2074,  2245,  2462,  2655,  2720,  2581,  2423,  2731,
       3145,  3383,  3436,  3909,  4448,  4422,  4032,  3938,  3665,  3461,
       3465,  3317,  2487,  1908,  1311,   683,    52,  -582, -1196, -1798,
      -2158, -2450, -2475, -2429, -2277, -2011, -2140, -2306, -2551, -2726,
      -3016, -3319, -3414},
    /* -50 */
    { -1615, -1938, -1875, -1827, -1839, -1793, -1605, -1650, -1737, -1773,
      -1580, -1237, -1010,  -983, -1051, -1025,  -838,  -653,  -316,    48,
        502,  1382,  1186,  1114,  1264,   785,   231,   329,   353,   556,
       1084,  1597,  2065,  2475,  2744,  2701,  2518,  2545,  2584,  2963,
       3323,  3537,  3792,  4085,  4520,  4505,  4459,  4287,  3818,  4112,
       3975,  3293,  2748,  2043,  1272,   569,  -207,  -898, -1498, -1990,
      -2242, -2358, -2212, -1968, -1843, -1695, -1705, -1688, -1400, -1177,
      -1013, -1168, -1615},
    /* -45 */
    {   338,   -20,  -606,  -849,  -777,  -838, -1123, -1322, -1485, -1503,
      -1413, -1203, -1077, -1004,  -960,  -829,  -662,  -371,   -88,   322,
        710,  1323,  1831,  1202,   908,    47,  -292,  -367,  -495,  -174,
        688,  1500,  2194,  2673,  2568,  2423,  2099,  2168,  2617,  2834,
       3254,  3328,  3443,  4442,  4639,  4588,  4524,  4223,  3575,  3187,
       3101,  2651,  2155,  1506,   774,   -55,  -961, -1719, -2355, -2719,
      -2731, -2670, -2430, -2026, -1715, -1477, -1144,  -901,  -646,  -303,
        871,   565,   338},
    /* -40 */
    {  2048,  1283,   637,   317,   109,  -156,  -679, -1023, -1186, -1277,
      -1275, -1202, -1282, -1150, -1022,  -881,  -690,  -300,   -84,   130,
        694,   937,  2220,  1511,  1341,   558,  -266,  -623,  -670,  -209,
        643,  1459,  2101,  2385,  2307,  2000,  1765,  1992,  2496,  2733,
       2941,  3431,  3298,  3327,  3877,  4306,  4069,  3446,  2844,  2601,
       2333,  1786,  1318,   599,  -238, -1184, -2098, -2786, -3250, -3406,
      -3351, -3095, -2741, -2101, -1482,  -148,  -201,   221,   491,  1179,
       1877,  1206,  2048},
    /* -35 */
    {  2833,  2556,  1700,  1059,   497,   -21,  -370,  -752,  -959, -1103,
      -1093, -1104, -1198, -1097,  -960,  -785,  -596,  -362,  -211,   103,
        739,  1300,  3029,  2021,  1712,  1269,   -23,  -616,  -701,  -255,
        684,  1237,  1701,  1903,  1696,  1789,  1795,  2034,  2398,  2561,
       3187,  2625,  2609,  2897,  2564,  3339,  3118,  3121,  2240,  2102,
       1529,   991,   387,  -559, -1464, -2380, -3138, -3999, -3899, -3446,
      -3473, -3300, -2823, -1043,   143,   970,  2058,  1555,  1940,  2621,
       3154,  3839,  2833},
    /* -30 */
    {  4772,  3089,  2257,  1381,   566,    64,  -136,  -612,  -868, -1186,
      -1309, -1131, -1033,  -903,  -780,  -625,  -443,  -242,   100,   269,
        815,  1489,  3633,  2424,  1810,  1138,   297,  -720,  -847,    -2,
        347,   579,  1025,  1408,  1504,  1686,  2165,  2353,  2599,  3182,
       3332,  3254,  3094,  2042,  1369,  1945,  1468,  1487,  1505,  1048,
        613,    26,  -904, -1757, -2512, -3190, -3751, -3941, -3939, -2896,
      -2222, -1766, -1442,    70,  1262,  2229,  3189,  2910,  3371,  3608,
       4379,  4520,  4772},
    /* -25 */
    {  4984,  2801,  2475,  1374,   798,   198,  -269,  -628, -1063, -1262,
      -1090,  -970,  -692,  -516,  -458,  -313,  -143,    19,   183,   403,
        837,  1650,  3640,  2990,  2084,   628,   422,  -597, -1130,  -712,
       -474,  -110,   446,  1043,  1349,  1571,  2008,  2572,  2405,  3175,
       2766,  2407,  2100,  1130,   367,   840,    89,   114,    49,   -25,
       -494, -1369, -2345, -3166, -3804, -4256, -4141, -3730, -3337, -1814,
       -901,  -388,   298,  1365,  2593,  3490,  4639,  4427,  4795,  4771,
       5325,  5202,  4984},
    /* -20 */
    {  4994,  5152,  2649,  1466,   935,   427,  -115,  -518,  -838, -1135,
      -1134,  -917,  -525,  -280,  -218,  -310,  -396,  -306,  -137,   148,
        811,  1643,  3496,  4189,  1958,   358,  -784,  -684,  -740,  -800,
       -579,  -638,   -49,   704,  1221,  1358,  1657,  1957,  2280,  2639,
       2157,  1246,   728,  -364, -1021,  -586, -1098, -1055, -1032, -1244,
      -2065, -3158, -4028, -4660, -4802, -4817, -4599, -3523, -2561, -1260,
        446,  1374,  2424,  3310,  4588,  5499,  5724,  5479,  5698,  5912,
       6400,  6116,  4994},
    /* -15 */
    {  4930,  4158,  2626,  1375,   902,   630,   150,  -275,  -667, -1005,
       -954,  -847,  -645,  -376,  -315,  -479,  -639,  -681,  -550,  -268,
        709,  2996,  4880,  2382,  1695,  -136,  -964, -1211, -1038, -1045,
       -695,  -595,    23,   733,  1107,  1318,  1348,  1376,  1630,  2240,
       1248,   454,  -737, -1252, -2001, -2513, -1416, -2169, -2269, -3089,
      -4063, -5194, -5715, -6105, -5700, -4873, -3919, -2834, -1393,  -112,
       1573,  3189,  3907,  4863,  5437,  6548,  6379,  6281,  6289,  5936,
       6501,  5794,  4930},
    /* -10 */
    {  3525,  2747,  2135,  1489,  1078,   739,   544,   -39,  -268,  -588,
       -917, -1025, -1087,  -940,  -771,  -923, -1177, -1114,  -919,  -383,
       -108,  2135,  2818,  1929,   386, -1097, -1911, -1619, -1226, -1164,
       -952,  -583,   399,  1070,  1280,  1345,  1117,   993,  1306,  1734,
        538,  -463, -1208, -1602, -2662, -3265, -3203, -3408, -3733, -5014,
      -6083, -7253, -7578, -7096, -6418, -4658, -2647,  -586,   -87,  1053,
       3840,  3336,  5240,  6253,  6898,  7070,  7727,  7146,  6209,  5826,
       5068,  4161,  3525},
    /* -5 */
    {  2454,  1869,  1656,  1759,  1404,  1263,  1012,   605,   108,  -511,
       -980, -1364, -1620, -1633, -1421, -1342, -1412, -1349, -1006,  -229,
       1711,  1293,  1960,   605,  -793, -2058, -2108, -2626, -1195,  -606,
       -513,  -108,   671,  1504,  1853,  1711,  1709,   940,   570,   296,
       -913, -1639, -1471, -1900, -3000, -4164, -4281, -4062, -5366, -6643,
      -7818, -8993, -9275, -8306, -6421, -4134, -1837,  1367,  2850,  4286,
       5551,  5599,  5402,  6773,  7736,  7024,  8161,  6307,  5946,  4747,
       3959,  3130,  2454},
    /* 0 */
    {  2128,  1774,  1532,  1470,  1613,  1589,  1291,   783,    79,  -676,
      -1296, -1941, -2298, -2326, -2026, -1738, -1412, -1052,  -406,    82,
       1463,  1899,  1352,  -170, -1336, -2446, -2593, -2328, -1863,  -833,
        245,  1005,  1355,  1896,  1913,  1888,  1723,  1642,   940,  -127,
      -1668, -1919, -1078, -1633, -2762, -4357, -4885, -5143, -6260, -7507,
      -8947, -10042, -10259, -8865, -6329, -3424,  -692,  1445,  3354,  5132,
       5983,  4978,  7602,  7274,  7231,  6941,  6240,  5903,  4944,  4065,
       3205,  2566,  2128},
    /* 5 */
    {  1632,  1459,  1243,  1450,  1643,  1432,   867,   283,  -420, -1316,
      -1993, -2614, -3012, -3016, -2555, -1933, -1256,  -688,  -133,   634,
       1369,  2095,   -92,  -858, -1946, -3392, -3666, -3110, -1839,  -371,
        674,  1221,  1657,  1994,  2689,  2577,  2020,  2126,  1997,   987,
       -739,  -989, -1107, -1369, -1914, -3312, -4871, -5365, -6171, -7732,
      -9393, -10088, -10568, -9022, -6053, -4104, -1296,   373,  2310,  4378,
       6279,  6294,  6999,  6852,  6573,  6302,  5473,  5208,  4502,  3445,
       2790,  2215,  1632},
    /* 10 */
    {  1285,  1050,  1212,  1439,  1055,   638,   140,  -351, -1115, -2060,
      -2904, -3593, -3930, -3694, -2924, -2006, -1145,  -441,   164,  1059,
         91,  -440, -1043, -2791, -4146, -4489, -4259, -3218, -1691,  -683,
        306,  1160,  1735,  3081,  3275,  2807,  2373,  2309,  2151,  1245,
        207,  -132,  -507,  -564,  -956, -1917, -3167, -5067, -5820, -7588,
      -9107, -9732, -9732, -8769, -6308, -4585, -2512,  -891,  1108,  3278,
       5183,  6391,  5985,  5969,  6049,  5616,  4527,  4156,  3531,  2776,
       2456,  1904,  1285},
    /* 15 */
    {   862,   804,   860,   969,   544,    89,  -417, -1008, -1641, -2608,
      -3607, -4234, -4482, -4100, -3232, -2092, -1105, -1092,   238,   330,
       -571, -1803, -2983, -3965, -5578, -4864, -3777, -2572, -1690,  -536,
        806,  2042,  2323,  3106,  3019,  2833,  2260,  2064,  2036,  1358,
       1030,   908,   391,   -54,  -377,  -885, -2172, -3359, -5309, -6686,
      -8058, -8338, -8695, -8322, -6404, -5003, -3420, -2060,  -255,  1833,
       4143,  4218,  4771,  5031,  5241,  5504,  4399,  3471,  2832,  2266,
       1643,  1190,   862},
    /* 20 */
    {   442,   488,   986,   877,   757,  1175,  -696, -1473, -2285, -3128,
      -3936, -4520, -4739, -4286, -3350, -2092,  -747, -1894, -1083, -1508,
      -2037, -2528, -4813, -6316, -4698, -4222, -3279, -1814, -1001,   212,
       1714,  2273,  2535,  3367,  3112,  2736,  3086,  2742,  2679,  2071,
       1422,  1333,   922,   619,   183,  -945, -3070, -3680, -4245, -5461,
      -6064, -6652, -6806, -6210, -5947, -5177, -3814, -2589, -1319,   551,
       2150,  3262,  3799,  4177,  4898,  4658,  4149,  2833,  2148,  1410,
        899,   551,   442},
    /* 25 */
    {  -248,    12,   716,   415,   327,  -187, -1103, -1729, -2469, -3296,
      -4040, -4545, -4642, -4232, -3466, -2064, -1667, -3232, -2660, -2685,
      -2789, -4262, -5208, -5084, -4935, -4077, -2622,  -804,   131,   946,
       1859,  2203,  3038,  3433,  3758,  3029,  2757,  3524,  3109,  2511,
       2300,  1554,  1316,  1114,   954,   -81, -2642, -3389, -3167, -4211,
      -4634, -5193, -6014, -6245, -5347, -5313, -3846, -3149, -2130,  -354,
       1573,  2760,  3310,  3713,  4594,  3862,  2827,  1939,  1019,   313,
       -142,  -378,  -248},
    /* 30 */
    {  -720,  -717,  -528,  -573,  -867, -1224, -1588, -2135, -2796, -3432,
      -4036, -4329, -4246, -3464, -2996, -2389, -2323, -2844, -2744, -2884,
      -3238, -4585, -5164, -4463, -4064, -3238, -1751,   150,  1657,  2501,
       3023,  3007,  3404,  3976,  4354,  4648,  3440,  2708,  2813,  2968,
       2611,  2104,  1606,  1808,  1086,  -392, -1793,  -689, -1527, -2765,
      -3766, -4709, -3687, -2800, -3375, -3793, -3365, -4182, -2385, -1115,
        785,  2302,  3020,  3564,  4178,  2993,  1940,  1081,   331,  -364,
       -683,  -690,  -720},
    /* 35 */
    { -1004, -1222, -1315, -1304, -1463, -1680, -2160, -2675, -3233, -3746,
      -4021, -4053, -3373, -3012, -2447, -2184, -2780, -3219, -2825, -3079,
      -3181, -4284, -4548, -3867, -3123, -2302,  -785,   943,  2687,  4048,
       4460,  4290,  4118,  4585,  4282,  4437,  4898,  3818,  3696,  3414,
       2299,  2057,   627,  1915,  1833,   451,   678,  -876, -1602, -2167,
      -3344, -2549, -2860, -3514, -4043, -4207, -4005, -3918, -3121, -1521,
        471,  2023,  2980,  3679,  3465,  2405,  1475,   553,  -142,  -880,
      -1178,  -963, -1004},
    /* 40 */
    { -1223, -1218, -1076, -1116, -1298, -1541, -2085, -2648, -3120, -3473,
      -3679, -3342, -2334, -1912, -1787, -1756, -2482, -3182, -3322, -3429,
      -3395, -3374, -3372, -3341, -2654, -1509,   105,  1620,  3250,  4603,
       5889,  5776,  5198,  4840,  4903,  5370,  5086,  4536,  4519,  4601,
       3395,  4032,  3890,  3537,  3113,  2183, -1769, -1552, -2856, -3694,
      -4092, -3614, -5468, -6518, -6597, -5911, -5476, -4465, -2802, -1076,
        232,  1769,  2305,  3018,  3768,  1721,  1694,   667,  -154,  -799,
      -1068, -1196, -1223},
    /* 45 */
    {  -634,  -460,  -330,  -267,  -413,  -818, -1310, -1763, -2352, -2738,
      -2632, -2685, -1929, -1340,  -737, -1441, -2254, -2685, -3358, -3488,
      -3635, -3187, -2665, -2142, -1515,  -124,  1727,  2798,  3965,  5065,
       6150,  6513,  6089,  5773,  5044,  4471,  4677,  5052,  3938,  4537,
       4425,  3652,  3063,  2178,  1267,    84, -1109, -1974, -2905, -3650,
      -4264, -4741, -4136, -6324, -5826, -5143, -4851, -4344, -3225, -1386,
          5,  1153,  2198,  2833,  2835,  2563,  1337,  1194,   503,  -329,
       -289,  -754,  -634},
    /* 50 */
    {  -578,   -40,   559,   880,   749,   464,     0,  -516, -1140, -1655,
      -1818, -1589, -1555, -1337, -1769, -1919, -2372, -2981, -3485, -3976,
      -3941, -3565, -2614, -2223, -1253,   802,  2406,  3239,  4434,  5428,
       6265,  6394,  6180,  5690,  5855,  5347,  4506,  4685,  4799,  4445,
       3972,  3165,  2745,  1601,  1084,    41, -1170, -1701, -1916, -2914,
      -3305, -3790, -4435, -4128, -4163, -4535, -4190, -3891, -2951, -1869,
       -414,   851,  1494,  2097,  2268,  1939,  2031,  2460,   638,   578,
        325,    98,  -578},
    /* 55 */
    {   -18,   482,   905,  1562,  1739,   983,  1097,   568,    34,  -713,
       -695, -1072, -1576, -1879, -2479, -2884, -3275, -3971, -4456, -4654,
      -4461, -3688, -2697, -1623,  -823,  1270,  2523,  3883,  4967,  5977,
       6049,  6149,  6095,  5776,  5820,  5575,  4642,  4099,  4025,  3462,
       2679,  2447,  1951,  1601,  1151,   663,   157,  -603,  -952, -1987,
      -2609, -3316, -3600, -3684, -3717, -3836, -4024, -3452, -2950, -1861,
       -903,    89,   975,  1499,  1560,  1601,  1922,  2031,  2326,   -58,
        506,  -177,   -18},
    /* 60 */
    {    93,   673,   969,  1168,  1498,  1486,  1439,  1165,  1128,   720,
          5,  -689, -1610, -2409, -3094, -3585, -4193, -4772, -4678, -4521,
      -4184, -2955, -2252,  -834,   503,  1676,  2882,  4130,  4892,  5611,
       6390,  6338,  6069,  5974,  5582,  5461,  4788,  4503,  4080,  2957,
       1893,  1773,  1586,  1544,  1136,  1026,   622,    50,  -389, -1484,
      -2123, -2625, -3028, -3143, -3366, -3288, -3396, -3069, -2770, -2605,
      -1663,  -555,    25,   491,  1168,  1395,  1641,  1597,  1426,  1299,
        921,  -160,    93},
    /* 65 */
    {   419,   424,   443,   723,   884,  1030,  1077,  1191,  1065,   734,
        265, -1052, -1591, -2136, -2773, -3435, -3988, -3978, -3698, -3509,
      -3370, -2490, -1347,  -263,  1647,  2582,  3291,  4802,  4447,  5609,
       5879,  6454,  6709,  6606,  5988,  5365,  5103,  4385,  3996,  3250,
       2526,  1766,  1817,  1751,  1275,   857,   636,    29,   -12,  -918,
      -1364, -1871, -2023, -2102, -2258, -2441, -2371, -2192, -1908, -1799,
      -1720, -1662,  -385,    86,   466,   880,   715,   834,  1010,  1105,
        877,   616,   419},
    /* 70 */
    {   242,    93,    98,    62,   -54,   -25,  -127,  -156,  -253,  -412,
       -805, -1106, -1506, -1773, -2464, -2829, -2740, -2579, -2559, -2271,
      -1849,  -853,   294,  1055,  2357,  2780,  2907,  3909,  4522,  5272,
       5594,  5903,  5966,  5930,  5592,  5188,  4878,  4561,  4190,  3834,
       2963,  2451,  1981,  1525,  1064,   694,   253,   -70,  -318,  -781,
       -979, -1048, -1274, -1413, -1175, -1313, -1449, -1206,  -850, -1087,
       -828,  -933,  -540,  -301,   -35,    53,   279,   267,   345,   371,
        334,   289,   242},
    /* 75 */
    {   128,   228,   376,    46,  -173,  -355,  -417,  -548,  -764,  -925,
       -419,  -950, -1185, -1102, -1293, -1355, -1075,  -713,  -365,   167,
        516,  1381,  1882,  1826,  1956,  2492,  3192,  3541,  3750,  4123,
       4462,  4592,  4472,  4705,  4613,  4559,  4340,  4392,  4144,  3973,
       3119,  2582,  2057,  1684,  1199,   834,   477,   325,   295,  -198,
       -459,  -670,  -706,  -677,  -766,  -852,  -939,  -905,  -637,  -601,
       -531,  -433,  -292,  -158,    88,    85,   118,   121,   147,   179,
        173,   149,   128},
    /* 80 */
    {   342,   293,   244,   159,    38,    20,    15,   -15,  -109,  -119,
       -240,  -182,    16,   397,   550,   264,   350,   670,   865,   681,
       1188,  1136,   703,  1153,  1930,  2412,  2776,  3118,  3351,  3634,
       3653,  3272,  3177,  3161,  3354,  3671,  3615,  3572,  3522,  3274,
       2914,  2682,  2426,  2185,  1845,  1584,  1297,  1005,   809,   507,
        248,   314,   230,    96,   149,   240,   274,   297,   153,   109,
        164,    91,   104,    43,    12,   153,   243,   170,   184,    59,
         99,   158,   342},
    /* 85 */
    {   912,   961,  1013,  1013,   997,  1032,  1026,  1050,  1072,  1132,
       1156,  1253,  1310,  1389,  1441,  1493,  1508,  1565,  1621,  1642,
       1768,  1888,  2036,  2089,  2117,  2106,  2010,  2120,  2276,  2376,
       2426,  2427,  2526,  2582,  2493,  2534,  2628,  2564,  2471,  2509,
       2407,  2332,  2214,  2122,  1987,  1855,  1714,  1619,  1517,  1474,
       1406,  1351,  1308,  1264,  1181,  1081,  1047,  1084,  1043,   964,
        851,   755,   732,   706,   697,   785,   864,   762,   686,   729,
        789,   856,   912},
    /* 90 */
    {  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,
       1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,
       1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,
       1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,
       1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,
       1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,
       1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,  1490,
       1490,  1490,  1490}
};


/* This table is wmm2015.  Values obtained from MagneticField, part of
 * geographiclib., by using devtools/get_mag_var_table.py
 *
 * magvar[][] has the magnetic variation (declination), in hundredths of
 * a degree, on a 5 degree by 5 * degree grid for the entire planet.
 *
 * This table is duplicated in gps/clienthelpers.py.  Keep them in sync.
 */
/* *INDENT-OFF* */
const short magvar[GEOID_ROW][GEOID_COL] = {

    /* -90 */
    {  14920,  14420,  13920,  13420,  12920,  12420,  11920,  11420,  10920,
       10420,   9920,   9420,   8920,   8420,   7920,   7420,   6920,   6420,
        5920,   5420,   4920,   4420,   3920,   3420,   2920,   2420,   1920,
        1420,    920,    420,    -80,   -580,  -1080,  -1580,  -2080,  -2580,
       -3080,  -3580,  -4080,  -4580,  -5080,  -5580,  -6080,  -6580,  -7080,
       -7580,  -8080,  -8580,  -9080,  -9580, -10080, -10580, -11080, -11580,
      -12080, -12580, -13080, -13580, -14080, -14580, -15080, -15580, -16080,
      -16580, -17080, -17580,  17920,  17420,  16920,  16420,  15920,  15420,
       14920},
    /* -85 */
    {  14174,  13609,  13052,  12504,  11965,  11435,  10914,  10402,   9898,
        9403,   8915,   8434,   7960,   7492,   7029,   6572,   6119,   5671,
        5226,   4784,   4346,   3909,   3475,   3042,   2611,   2180,   1750,
        1319,    889,    457,     25,   -409,   -845,  -1283,  -1723,  -2166,
       -2612,  -3061,  -3514,  -3971,  -4431,  -4895,  -5364,  -5837,  -6315,
       -6798,  -7285,  -7778,  -8277,  -8781,  -9291,  -9808, -10331, -10860,
      -11397, -11940, -12491, -13048, -13613, -14184, -14761, -15344, -15931,
      -16523, -17117, -17714,  17690,  17094,  16500,  15910,  15325,  14746,
       14174},
    /* -80 */
    {  12958,  12331,  11733,  11163,  10619,  10099,   9600,   9121,   8659,
        8211,   7776,   7352,   6937,   6529,   6127,   5730,   5338,   4949,
        4563,   4180,   3799,   3420,   3043,   2667,   2292,   1918,   1544,
        1170,    795,    419,     40,   -342,   -727,  -1117,  -1512,  -1912,
       -2318,  -2729,  -3147,  -3571,  -4002,  -4438,  -4880,  -5328,  -5782,
       -6241,  -6707,  -7180,  -7659,  -8146,  -8641,  -9146,  -9662, -10190,
      -10732, -11290, -11866, -12461, -13077, -13716, -14379, -15064, -15772,
      -16500, -17244, -17999,  17241,  16485,  15738,  15007,  14298,  13614,
       12958},
    /* -75 */
    {  11045,  10435,   9882,   9378,   8915,   8485,   8081,   7699,   7335,
        6983,   6640,   6304,   5972,   5642,   5313,   4982,   4651,   4317,
        3982,   3646,   3309,   2972,   2635,   2300,   1966,   1634,   1304,
         975,    646,    316,    -16,   -352,   -694,  -1042,  -1398,  -1764,
       -2138,  -2523,  -2916,  -3319,  -3729,  -4146,  -4569,  -4997,  -5430,
       -5866,  -6306,  -6750,  -7197,  -7650,  -8109,  -8576,  -9054,  -9545,
      -10054, -10585, -11143, -11736, -12371, -13058, -13806, -14624, -15519,
      -16489, -17525,  17396,  16307,  15245,  14242,  13317,  12478,  11723,
       11045},
    /* -70 */
    {   8567,   8144,   7771,   7437,   7132,   6851,   6587,   6336,   6095,
        5858,   5623,   5386,   5144,   4896,   4639,   4372,   4094,   3807,
        3510,   3206,   2895,   2582,   2266,   1952,   1641,   1334,   1033,
         736,    444,    155,   -136,   -429,   -729,  -1038,  -1359,  -1693,
       -2042,  -2406,  -2782,  -3170,  -3568,  -3973,  -4383,  -4795,  -5208,
       -5620,  -6030,  -6437,  -6843,  -7246,  -7650,  -8054,  -8463,  -8880,
       -9308,  -9756, -10230, -10744, -11314, -11965, -12737, -13686, -14889,
      -16424,  17708,  15701,  13867,  12382,  11234,  10344,   9636,   9056,
        8567},
    /* -65 */
    {   6318,   6126,   5946,   5777,   5617,   5466,   5322,   5183,   5046,
        4910,   4770,   4622,   4464,   4291,   4101,   3891,   3661,   3411,
        3142,   2857,   2558,   2250,   1938,   1625,   1318,   1019,    730,
         454,    189,    -67,   -318,   -570,   -827,  -1096,  -1381,  -1685,
       -2009,  -2354,  -2717,  -3095,  -3485,  -3881,  -4279,  -4676,  -5068,
       -5453,  -5828,  -6193,  -6546,  -6887,  -7218,  -7538,  -7849,  -8152,
       -8450,  -8744,  -9037,  -9335,  -9645,  -9979, -10368, -10886, -11809,
      -15193,  10922,   8778,   8037,   7595,   7264,   6988,   6744,   6523,
        6318},
    /* -60 */
    {   4768,   4709,   4640,   4566,   4490,   4416,   4344,   4274,   4208,
        4141,   4071,   3993,   3902,   3794,   3663,   3505,   3318,   3100,
        2852,   2578,   2280,   1965,   1640,   1313,    991,    682,    392,
         123,   -125,   -354,   -569,   -778,   -991,  -1215,  -1458,  -1726,
       -2023,  -2347,  -2695,  -3064,  -3445,  -3833,  -4220,  -4600,  -4969,
       -5323,  -5658,  -5972,  -6264,  -6532,  -6776,  -6993,  -7181,  -7338,
       -7458,  -7532,  -7546,  -7474,  -7273,  -6857,  -6058,  -4557,  -2058,
         727,   2633,   3688,   4259,   4572,   4736,   4812,   4830,   4812,
        4768},
    /* -55 */
    {   3771,   3768,   3750,   3720,   3685,   3647,   3611,   3577,   3546,
        3519,   3492,   3462,   3421,   3364,   3283,   3171,   3023,   2835,
        2607,   2339,   2036,   1705,   1356,   1001,    652,    319,     13,
        -261,   -501,   -709,   -891,  -1058,  -1222,  -1394,  -1587,  -1810,
       -2069,  -2365,  -2694,  -3048,  -3419,  -3795,  -4168,  -4529,  -4871,
       -5189,  -5479,  -5737,  -5961,  -6148,  -6293,  -6394,  -6445,  -6436,
       -6355,  -6184,  -5896,  -5455,  -4817,  -3939,  -2818,  -1532,   -251,
         866,   1747,   2404,   2878,   3214,   3446,   3600,   3697,   3750,
        3771},
    /* -50 */
    {   3099,   3120,   3123,   3115,   3098,   3077,   3056,   3036,   3021,
        3011,   3006,   3001,   2992,   2971,   2929,   2856,   2743,   2584,
        2373,   2111,   1800,   1449,   1071,    680,    294,    -71,   -401,
        -686,   -925,  -1118,  -1273,  -1401,  -1516,  -1633,  -1767,  -1932,
       -2138,  -2392,  -2689,  -3021,  -3374,  -3734,  -4089,  -4425,  -4736,
       -5014,  -5253,  -5450,  -5601,  -5700,  -5743,  -5724,  -5634,  -5465,
       -5202,  -4832,  -4344,  -3732,  -3008,  -2203,  -1368,   -556,    188,
         839,   1387,   1838,   2201,   2485,   2704,   2865,   2979,   3054,
        3099},
    /* -45 */
    {   2611,   2642,   2656,   2658,   2651,   2639,   2625,   2610,   2598,
        2592,   2591,   2595,   2601,   2600,   2583,   2539,   2456,   2321,
        2128,   1871,   1554,   1183,    774,    346,    -76,   -472,   -823,
       -1120,  -1357,  -1540,  -1675,  -1774,  -1850,  -1915,  -1987,  -2083,
       -2220,  -2411,  -2657,  -2951,  -3276,  -3612,  -3941,  -4248,  -4522,
       -4755,  -4940,  -5073,  -5147,  -5158,  -5102,  -4972,  -4762,  -4467,
       -4083,  -3612,  -3068,  -2474,  -1858,  -1251,   -675,   -141,    343,
         777,   1161,   1495,   1781,   2020,   2214,   2366,   2479,   2559,
        2611},
    /* -40 */
    {   2236,   2269,   2287,   2295,   2295,   2289,   2279,   2266,   2253,
        2243,   2238,   2239,   2245,   2250,   2246,   2220,   2157,   2043,
        1864,   1613,   1290,    903,    468,     12,   -438,   -854,  -1216,
       -1513,  -1745,  -1916,  -2038,  -2121,  -2172,  -2202,  -2220,  -2243,
       -2294,  -2397,  -2566,  -2798,  -3077,  -3376,  -3672,  -3945,  -4180,
       -4367,  -4499,  -4570,  -4575,  -4509,  -4370,  -4157,  -3867,  -3504,
       -3076,  -2600,  -2099,  -1602,  -1131,   -697,   -301,     62,    396,
         706,    992,   1252,   1485,   1688,   1860,   1999,   2106,   2183,
        2236},
    /* -35 */
    {   1934,   1966,   1985,   1996,   2001,   2000,   1994,   1982,   1967,
        1952,   1939,   1932,   1930,   1932,   1930,   1911,   1860,   1758,
        1589,   1343,   1016,    618,    167,   -305,   -767,  -1187,  -1544,
       -1830,  -2047,  -2204,  -2315,  -2387,  -2426,  -2432,  -2406,  -2360,
       -2315,  -2308,  -2368,  -2508,  -2716,  -2964,  -3222,  -3462,  -3666,
       -3818,  -3909,  -3934,  -3888,  -3771,  -3584,  -3331,  -3015,  -2645,
       -2236,  -1809,  -1390,  -1003,   -658,   -355,    -85,    165,    403,
         634,    856,   1066,   1261,   1436,   1588,   1714,   1813,   1885,
        1934},
    /* -30 */
    {   1687,   1715,   1732,   1744,   1752,   1755,   1753,   1744,   1728,
        1709,   1689,   1672,   1660,   1653,   1647,   1629,   1581,   1485,
        1321,   1075,    746,    343,   -113,   -588,  -1045,  -1453,  -1792,
       -2056,  -2251,  -2390,  -2484,  -2542,  -2565,  -2544,  -2475,  -2360,
       -2220,  -2093,  -2026,  -2049,  -2164,  -2349,  -2568,  -2786,  -2976,
       -3116,  -3193,  -3201,  -3138,  -3007,  -2815,  -2567,  -2271,  -1936,
       -1577,  -1218,   -883,   -590,   -345,   -140,     41,    212,    385,
         563,    742,    918,   1085,   1238,   1374,   1488,   1578,   1643,
        1687},
    /* -25 */
    {   1485,   1507,   1520,   1530,   1539,   1544,   1545,   1539,   1525,
        1504,   1479,   1454,   1434,   1420,   1407,   1385,   1336,   1239,
        1074,    827,    497,     93,   -359,   -823,  -1262,  -1647,  -1960,
       -2198,  -2366,  -2478,  -2546,  -2575,  -2564,  -2502,  -2380,  -2200,
       -1978,  -1753,  -1575,  -1486,  -1504,  -1617,  -1794,  -1998,  -2189,
       -2341,  -2432,  -2454,  -2406,  -2295,  -2131,  -1920,  -1670,  -1388,
       -1089,   -796,   -531,   -312,   -141,     -8,    108,    224,    353,
         495,    646,    798,    945,   1082,   1204,   1308,   1389,   1447,
        1485},
    /* -20 */
    {   1322,   1337,   1344,   1350,   1356,   1363,   1366,   1363,   1351,
        1330,   1303,   1275,   1249,   1230,   1212,   1186,   1132,   1030,
         859,    609,    278,   -121,   -561,  -1006,  -1420,  -1775,  -2058,
       -2264,  -2401,  -2478,  -2506,  -2489,  -2425,  -2309,  -2134,  -1903,
       -1633,  -1359,  -1122,   -961,   -902,   -945,  -1074,  -1254,  -1445,
       -1612,  -1727,  -1778,  -1764,  -1691,  -1570,  -1409,  -1213,   -986,
        -743,   -504,   -294,   -131,    -15,     66,    135,    213,    311,
         430,    563,    699,    832,    957,   1070,   1165,   1240,   1291,
        1322},
    /* -15 */
    {   1194,   1201,   1202,   1201,   1204,   1209,   1213,   1211,   1202,
        1183,   1157,   1128,   1101,   1080,   1059,   1028,    968,    858,
         680,    425,     94,   -296,   -718,  -1138,  -1522,  -1846,  -2096,
       -2268,  -2367,  -2401,  -2378,  -2304,  -2182,  -2013,  -1798,  -1545,
       -1270,   -996,   -752,   -566,   -462,   -451,   -529,   -673,   -849,
       -1019,  -1151,  -1228,  -1246,  -1212,  -1135,  -1023,   -876,   -700,
        -505,   -311,   -145,    -23,     53,     97,    133,    184,    262,
         366,    488,    615,    739,    856,    963,   1054,   1124,   1170,
        1194},
    /* -10 */
    {   1097,   1098,   1090,   1083,   1081,   1083,   1087,   1087,   1080,
        1063,   1039,   1011,    986,    965,    943,    906,    838,    718,
         531,    271,    -56,   -434,   -835,  -1226,  -1578,  -1869,  -2085,
       -2221,  -2279,  -2265,  -2188,  -2059,  -1886,  -1678,  -1446,  -1198,
        -946,   -703,   -482,   -302,   -181,   -135,   -170,   -275,   -425,
        -583,   -719,   -810,   -851,   -848,   -808,   -736,   -633,   -500,
        -346,   -191,    -59,     32,     78,     96,    109,    141,    205,
         300,    415,    537,    658,    772,    877,    966,   1035,   1078,
        1097},
    /* -5 */
    {   1026,   1022,   1009,    995,    988,    987,    990,    992,    987,
         973,    951,    926,    903,    882,    856,    813,    734,    603,
         407,    144,   -179,   -542,   -919,  -1281,  -1601,  -1858,  -2038,
       -2136,  -2152,  -2094,  -1971,  -1797,  -1589,  -1363,  -1131,   -903,
        -685,   -480,   -293,   -131,    -10,     55,     50,    -22,   -143,
        -284,   -412,   -507,   -561,   -578,   -565,   -525,   -457,   -360,
        -242,   -121,    -18,     47,     71,     69,     66,     85,    140,
         229,    339,    460,    580,    695,    803,    895,    966,   1010,
        1026},
    /* 0 */
    {    975,    971,    953,    935,    924,    922,    926,    930,    928,
         917,    898,    875,    852,    828,    796,    743,    651,    506,
         300,     34,   -282,   -629,   -981,  -1314,  -1601,  -1823,  -1967,
       -2028,  -2006,  -1910,  -1753,  -1553,  -1327,  -1096,   -873,   -669,
        -484,   -315,   -160,    -20,     94,    166,    181,    134,     38,
         -84,   -201,   -293,   -352,   -380,   -384,   -367,   -327,   -261,
        -175,    -84,     -9,     33,     39,     23,      8,     17,     64,
         147,    255,    375,    497,    617,    731,    831,    908,    957,
         975},
    /* 5 */
    {    937,    936,    920,    901,    890,    888,    895,    902,    904,
         897,    880,    858,    832,    803,    761,    693,    584,    423,
         205,    -65,   -374,   -704,  -1031,  -1333,  -1587,  -1773,  -1881,
       -1906,  -1853,  -1730,  -1554,  -1341,  -1112,   -885,   -675,   -490,
        -331,   -191,    -62,     58,    162,    235,    260,    231,    154,
          50,    -56,   -142,   -201,   -234,   -249,   -248,   -228,   -188,
        -130,    -67,    -18,      2,     -9,    -38,    -64,    -63,    -24,
          52,    157,    276,    402,    529,    652,    763,    852,    911,
         937},
    /* 10 */
    {    901,    911,    902,    889,    881,    883,    895,    908,    916,
         913,    899,    876,    845,    806,    749,    662,    532,    352,
         120,   -155,   -459,   -773,  -1076,  -1347,  -1565,  -1715,  -1787,
       -1781,  -1703,  -1564,  -1380,  -1167,   -942,   -724,   -526,   -357,
        -216,    -96,     13,    117,    210,    280,    312,    294,    232,
         142,     47,    -33,    -89,   -125,   -145,   -153,   -149,   -129,
         -97,    -61,    -38,    -40,    -67,   -110,   -146,   -156,   -127,
         -58,     40,    159,    289,    424,    559,    683,    787,    861,
         901},
    /* 15 */
    {    859,    887,    893,    892,    893,    904,    924,    945,    959,
         962,    950,    925,    888,    836,    760,    651,    497,    294,
          45,   -238,   -540,   -841,  -1120,  -1360,  -1542,  -1655,  -1694,
       -1661,  -1565,  -1418,  -1234,  -1028,   -812,   -604,   -416,   -257,
        -128,    -21,     75,    165,    249,    315,    349,    340,    290,
         212,    127,     53,      0,    -35,    -58,    -73,    -80,    -78,
         -68,    -58,    -61,    -85,   -131,   -189,   -238,   -260,   -243,
        -185,    -94,     22,    155,    298,    445,    584,    706,    799,
         859},
    /* 20 */
    {    803,    856,    885,    903,    921,    945,    976,   1006,   1029,
        1037,   1027,   1000,    955,    888,    793,    658,    478,    250,
         -18,   -314,   -617,   -908,  -1167,  -1377,  -1524,  -1602,  -1610,
       -1554,  -1446,  -1296,  -1117,   -920,   -715,   -516,   -336,   -184,
         -60,     40,    127,    208,    284,    346,    381,    379,    339,
         273,    198,    131,     81,     46,     22,      3,    -14,    -27,
         -38,    -54,    -83,   -131,   -198,   -273,   -338,   -374,   -371,
        -326,   -244,   -131,      3,    153,    310,    464,    605,    720,
         803},
    /* 25 */
    {    731,    813,    871,    916,    957,   1000,   1045,   1086,   1117,
        1131,   1123,   1094,   1040,    958,    842,    682,    475,    221,
         -70,   -381,   -690,   -976,  -1218,  -1402,  -1518,  -1565,  -1546,
       -1472,  -1354,  -1203,  -1030,   -841,   -646,   -455,   -281,   -130,
          -8,     90,    173,    249,    318,    377,    413,    418,    389,
         336,    273,    214,    167,    134,    107,     83,     57,     29,
          -4,    -45,   -101,   -176,   -267,   -361,   -444,   -497,   -509,
        -477,   -404,   -296,   -162,     -7,    159,    326,    483,    621,
         731},
    /* 30 */
    {    643,    756,    848,    926,    996,   1062,   1124,   1178,   1217,
        1235,   1230,   1197,   1134,   1038,    902,    718,    484,    203,
        -113,   -443,   -762,  -1046,  -1276,  -1439,  -1530,  -1551,  -1511,
       -1422,  -1297,  -1145,   -975,   -792,   -604,   -418,   -246,    -96,
          29,    129,    213,    287,    354,    411,    450,    464,    449,
         411,    362,    313,    272,    238,    208,    176,    139,     94,
          38,    -31,   -117,   -220,   -337,   -454,   -556,   -626,   -654,
        -634,   -569,   -466,   -331,   -174,     -1,    176,    348,    506,
         643},
    /* 35 */
    {    547,    691,    818,    930,   1031,   1123,   1205,   1273,   1321,
        1345,   1340,   1304,   1234,   1124,    968,    760,    499,    189,
        -153,   -505,   -837,  -1125,  -1347,  -1495,  -1567,  -1570,  -1514,
       -1413,  -1281,  -1126,   -956,   -776,   -590,   -406,   -233,    -80,
          50,    157,    246,    323,    392,    452,    498,    523,    525,
         507,    476,    440,    405,    371,    335,    292,    239,    173,
          90,    -10,   -129,   -265,   -411,   -553,   -675,   -761,   -801,
        -792,   -734,   -633,   -499,   -338,   -161,     25,    210,    386,
         547},
    /* 40 */
    {    454,    626,    784,    929,   1061,   1180,   1283,   1366,   1424,
        1454,   1452,   1413,   1335,   1211,   1036,    804,    513,    173,
        -199,   -576,   -925,  -1219,  -1439,  -1577,  -1637,  -1627,  -1560,
       -1450,  -1311,  -1152,   -978,   -795,   -607,   -422,   -245,    -85,
          54,    172,    271,    358,    435,    504,    562,    603,    627,
         632,    622,    602,    575,    541,    497,    439,    365,    271,
         156,     19,   -139,   -312,   -489,   -657,   -799,   -900,   -950,
        -947,   -892,   -792,   -656,   -492,   -310,   -117,     79,    271,
         454},
    /* 45 */
    {    374,    569,    754,    927,   1087,   1230,   1354,   1454,   1525,
        1563,   1565,   1525,   1439,   1301,   1105,    845,    522,    145,
        -262,   -668,  -1037,  -1341,  -1562,  -1696,  -1748,  -1729,  -1654,
       -1538,  -1392,  -1225,  -1045,   -855,   -661,   -469,   -284,   -114,
          39,    172,    289,    392,    486,    572,    648,    712,    761,
         793,    808,    807,    790,    756,    702,    627,    526,    397,
         241,     59,   -144,   -359,   -573,   -769,   -930,  -1043,  -1099,
       -1098,  -1042,   -940,   -799,   -630,   -442,   -241,    -34,    172,
         374},
    /* 50 */
    {    313,    526,    732,    928,   1110,   1276,   1419,   1536,   1622,
        1671,   1679,   1641,   1549,   1396,   1175,    882,    518,     96,
        -355,   -797,  -1192,  -1508,  -1733,  -1864,  -1910,  -1885,  -1804,
       -1680,  -1526,  -1350,  -1159,   -960,   -755,   -552,   -354,   -168,
           4,    160,    301,    430,    549,    661,    764,    857,    935,
         997,   1040,   1060,   1055,   1023,    960,    864,    731,    561,
         355,    119,   -139,   -405,   -660,   -886,  -1066,  -1187,  -1245,
       -1241,  -1181,  -1072,   -925,   -749,   -552,   -342,   -125,     94,
         313},
    /* 55 */
    {    270,    499,    721,    935,   1135,   1319,   1480,   1614,   1716,
        1779,   1797,   1761,   1664,   1495,   1245,    909,    490,      9,
        -500,   -988,  -1413,  -1744,  -1971,  -2099,  -2138,  -2105,  -2015,
       -1881,  -1716,  -1528,  -1324,  -1110,   -890,   -670,   -454,   -246,
         -48,    137,    311,    475,    629,    776,    914,   1041,   1155,
        1250,   1322,   1366,   1377,   1351,   1281,   1164,    997,    779,
         514,    211,   -114,   -441,   -744,  -1003,  -1201,  -1329,  -1387,
       -1377,  -1308,  -1191,  -1034,   -849,   -642,   -422,   -194,     38,
         270},
    /* 60 */
    {    240,    482,    719,    946,   1162,   1361,   1539,   1690,   1808,
        1886,   1914,   1882,   1779,   1590,   1302,    908,    415,   -149,
        -734,  -1280,  -1738,  -2081,  -2305,  -2422,  -2447,  -2399,  -2294,
       -2145,  -1963,  -1758,  -1536,  -1302,  -1062,   -819,   -577,   -340,
        -110,    113,    326,    532,    730,    919,   1099,   1267,   1420,
        1551,   1657,   1730,   1764,   1749,   1680,   1548,   1349,   1080,
         747,    364,    -44,   -446,   -809,  -1108,  -1328,  -1464,  -1519,
       -1502,  -1424,  -1297,  -1130,   -934,   -717,   -487,   -247,     -4,
         240},
    /* 65 */
    {    214,    468,    717,    957,   1186,   1399,   1592,   1759,   1891,
        1982,   2018,   1988,   1872,   1652,   1308,    830,    230,   -448,
       -1130,  -1738,  -2220,  -2559,  -2763,  -2853,  -2849,  -2773,  -2641,
       -2467,  -2260,  -2030,  -1782,  -1523,  -1255,   -983,   -709,   -437,
        -168,     96,    355,    608,    853,   1090,   1316,   1529,   1725,
        1899,   2044,   2155,   2220,   2232,   2178,   2047,   1828,   1514,
        1111,    635,    124,   -376,   -819,  -1174,  -1426,  -1577,  -1635,
       -1615,  -1530,  -1394,  -1218,  -1013,   -787,   -546,   -296,    -42,
         214},
    /* 70 */
    {    178,    442,    702,    955,   1195,   1421,   1625,   1802,   1944,
        2040,   2074,   2030,   1881,   1600,   1159,    546,   -215,  -1041,
       -1821,  -2461,  -2924,  -3215,  -3362,  -3394,  -3337,  -3214,  -3040,
       -2827,  -2585,  -2320,  -2040,  -1747,  -1445,  -1138,   -828,   -517,
        -207,    101,    406,    706,    999,   1283,   1558,   1819,   2062,
        2284,   2479,   2637,   2752,   2811,   2799,   2700,   2496,   2170,
        1716,   1147,    511,   -125,   -689,  -1136,  -1450,  -1637,  -1714,
       -1702,  -1619,  -1481,  -1302,  -1092,   -859,   -611,   -352,    -88,
         178},
    /* 75 */
    {    114,    385,    652,    911,   1157,   1386,   1592,   1767,   1899,
        1975,   1973,   1868,   1622,   1193,    548,   -307,  -1284,  -2226,
       -2997,  -3542,  -3876,  -4038,  -4070,  -4006,  -3869,  -3677,  -3445,
       -3182,  -2895,  -2589,  -2269,  -1939,  -1600,  -1257,   -909,   -559,
        -210,    140,    486,    829,   1167,   1497,   1818,   2128,   2423,
        2698,   2951,   3173,   3356,   3491,   3562,   3550,   3431,   3177,
        2759,   2165,   1420,    605,   -165,   -797,  -1254,  -1540,  -1684,
       -1714,  -1659,  -1539,  -1370,  -1166,   -936,   -687,   -426,   -157,
         114},
    /* 80 */
    {     -8,    249,    501,    742,    965,   1164,   1327,   1442,   1489,
        1441,   1262,    902,    306,   -553,  -1618,  -2716,  -3648,  -4317,
       -4731,  -4941,  -4998,  -4945,  -4811,  -4617,  -4378,  -4104,  -3804,
       -3483,  -3146,  -2796,  -2436,  -2068,  -1693,  -1315,   -933,   -549,
        -164,    220,    603,    984,   1361,   1733,   2099,   2457,   2805,
        3140,   3459,   3758,   4031,   4273,   4472,   4616,   4686,   4657,
        4492,   4146,   3573,   2755,   1755,    731,   -142,   -778,  -1181,
       -1395,  -1468,  -1438,  -1333,  -1176,   -980,   -758,   -517,   -265,
          -8},
    /* 85 */
    {   -546,   -534,   -552,   -627,   -794,  -1097,  -1585,  -2300,  -3236,
       -4294,  -5297,  -6100,  -6655,  -6988,  -7144,  -7167,  -7093,  -6945,
       -6742,  -6496,  -6217,  -5912,  -5585,  -5241,  -4883,  -4513,  -4134,
       -3746,  -3352,  -2952,  -2547,  -2138,  -1726,  -1312,   -896,   -478,
         -60,    359,    778,   1196,   1612,   2028,   2441,   2851,   3257,
        3660,   4057,   4448,   4831,   5205,   5567,   5916,   6248,   6558,
        6841,   7088,   7287,   7421,   7465,   7378,   7100,   6543,   5611,
        4289,   2795,   1493,    572,      0,   -325,   -488,   -553,   -561,
        -546},
    /* 90 */
    { -17825, -17325, -16825, -16325, -15825, -15325, -14825, -14325, -13825,
      -13325, -12825, -12325, -11825, -11325, -10825, -10325,  -9825,  -9325,
       -8825,  -8325,  -7825,  -7325,  -6825,  -6325,  -5825,  -5325,  -4825,
       -4325,  -3825,  -3325,  -2825,  -2325,  -1825,  -1325,   -825,   -325,
         175,    675,   1175,   1675,   2175,   2675,   3175,   3675,   4175,
        4675,   5175,   5675,   6175,   6675,   7175,   7675,   8175,   8675,
        9175,   9675,  10175,  10675,  11175,  11675,  12175,  12675,  13175,
       13675,  14175,  14675,  15175,  15675,  16175,  16675,  17175,  17675,
      -17825}
};
/* *INDENT-ON* */
void gpsd_sincos(double x, double *sinp, double *cosp)
{
    *sinp = sin(x);
    *cosp = cos(x);
}

/* compute bilinear approximation
 * since our data set rows and columns are integer degree, the corner
 * points can be integers.
 *
 * since the geoid_delta is shorts, the z11, z12, z21 an z22 can be ints.
 */
static double bilinear(int x1, int y1, int x2, int y2, double x,
                       double y, int z11, int z12, int z21,
                       int z22)
{
    int delta;
    double xx1 = x - x1;
    double x2x = x2 - x;
    double yy1 = y - y1;
    double y2y = y2 - y;

    /* handle some corner cases */
    if (y1 == y2) {
       if (x1 == x2)
           return z11;
       else
           return (z22 * (xx1) + z11 * (x2x)) / (x2 - x1);
    } else if (x1 == x2) {
       return (z22 * (yy1) + z11 * (y2y)) / (y2 - y1);
    }

    delta = (y2 - y1) * (x2 - x1);

//#ifdef __UNUSED
//    fprintf(stderr, "x1 %d x2 %d y1 %d y2 %d delta %d\n",
//            x1, x2, y1, y2, delta);
//#endif

    return (z22 * yy1 * xx1 + z12 * y2y * xx1 +
            z21 * yy1 * x2x + z11 * y2y * x2x) / delta;
}


/* return geoid separation (MSL-WGS84) in meters, given a lat/lon in degrees.
 * Online calculator here:
 * https://geographiclib.sourceforge.io/cgi-bin/GeoidEval
 *
 * Or use GeoidEval from the geographiclib.
 *
 * The value for any lat/lon is computed from the 5x5 using bilinear
 * interpolation.  Should probably use cubic interpolation as GeoidEval
 * does by default?
 *
 * Calculated separation can differ from geoidEval by up to 12m!
 */
double wgs84_separation(double lat, double lon)
{
    int ilat, ilon;
    int ilat1, ilat2, ilon1, ilon2;
    const int span = 5;

    if (0 == isfinite(lat) ||
        0 == isfinite(lon)) {
        return 0.0;
    }

    /* ilat is 0 to 18
     * lat -90 (90S) is ilat 0
     * lat 0 is ilat 9
     * lat 90 (90N) is ilat 18 */
    ilat = (int)floor((90. + lat) / span);
    /* ilon is 0 to 36
     * lon -180 is ilon 0
     * long 0 (Prime Median) is ilon 18
     * long 180 is ilon 36 */
    ilon = (int)floor((180. + lon) / span);

    /* sanity checks to prevent segfault on bad data */
    if ((GEOID_ROW <= ilat) || (0 > ilat) ||
        (GEOID_COL <= ilon) || (0 > ilon))
        return 0.0;

    ilat1 = ilat;
    ilon1 = ilon;
    ilat2 = (ilat < GEOID_ROW - 1) ? ilat + 1 : ilat;
    ilon2 = (ilon < GEOID_COL - 1) ? ilon + 1 : ilon;

//#ifdef __UNUSED
//    fprintf(stderr, "ilat1 %3d lat %9.4f ilat2 %3d\n"
//                    "ilon1 %3d lon %9.4f ilon2 %3d\n",
//                    ilat1, lat, ilat2,
//                    ilon1, lon, ilon2);
//#endif

    /* the "/ 100" is to convert cm to meters */
    return bilinear(ilon1 * span - 180, ilat1 * span - 90,
                    ilon2 * span - 180, ilat2 * span - 90,
                    lon, lat,
                    geoid_delta[ilat1][ilon1],
                    geoid_delta[ilat1][ilon2],
                    geoid_delta[ilat2][ilon1],
                    geoid_delta[ilat2][ilon2]
        ) / 100;
}


/* return magnetic variation (deviation) degrees, given a lat/lon in degrees.
 * Online calculator here:
 * https://www.ngdc.noaa.gov/geomag/calculators/magcalc.shtml
 *
 * Or use MagneticField from the geographiclib.
 *
 * The value for any lat/lon is computed from the 5x5 using bilinear
 * interpolation.
 *
 * Substantially similar code to wgs84_separation() but may
 * diverge eventually, so separate.
 *
 */
double mag_var(double lat, double lon)
{
    int ilat, ilon;
    int ilat1, ilat2, ilon1, ilon2;
    const int span = 5;

    if (0 == isfinite(lat) ||
        0 == isfinite(lon)) {
        return 0.0;
    }

    /* ilat is 0 to 18
     * lat -90 (90S) is ilat 0
     * lat 0 is ilat 9
     * lat 90 (90N) is ilat 18 */
    ilat = (int)floor((90. + lat) / span);
    /* ilon is 0 to 36
     * lon -180 is ilon 0
     * long 0 (Prime Median) is ilon 18
     * long 180 is ilon 36 */
    ilon = (int)floor((180. + lon) / span);

    /* sanity checks to prevent segfault on bad data */
    if ((GEOID_ROW <= ilat) || (0 > ilat) ||
        (GEOID_COL <= ilon) || (0 > ilon))
        return 0.0;

    ilat1 = ilat;
    ilon1 = ilon;
    ilat2 = (ilat < GEOID_ROW - 1) ? ilat + 1 : ilat;
    ilon2 = (ilon < GEOID_COL - 1) ? ilon + 1 : ilon;

//#ifdef __UNUSED
//    fprintf(stderr, "ilat1 %3d lat %9.4f ilat2 %3d\n"
//                    "ilon1 %3d lon %9.4f ilon2 %3d\n",
//                    ilat1, lat, ilat2,
//                    ilon1, lon, ilon2);
//    fprintf(stderr, "12 %3d   22 %3d\n"
//                    "11 %3d   12 %3d\n",
//                    magvar[ilat1][ilon1],
//                    magvar[ilat1][ilon2],
//                    magvar[ilat2][ilon1],
//                    magvar[ilat2][ilon2]);
//#endif

    /* the "/ 100" is to convert cm to meters */
    return bilinear(ilon1 * span - 180, ilat1 * span - 90,
                    ilon2 * span - 180, ilat2 * span - 90,
                    lon, lat,
                    magvar[ilat1][ilon1],
                    magvar[ilat1][ilon2],
                    magvar[ilat2][ilon1],
                    magvar[ilat2][ilon2]
        ) / 100;
}


/* fill in WGS84 position/velocity fields from ECEF coordinates
 * x, y, z are all in meters
 * vx, vy, vz are all in meters/second
 */
gps_mask_t ecef_to_wgs84fix(struct gps_fix_t *fix,
                            double x, double y, double z,
                            double vx, double vy, double vz)
{
    double lambda, phi, p, theta, n, vnorth, veast, vup;
    double cos_lambda, sin_lambda;
    double cos_phi, sin_phi;
    double cos_theta, sin_theta;
    gps_mask_t mask = 0;

    if (0 == isfinite(x) ||
        0 == isfinite(y) ||
        0 == isfinite(z)) {
        /* invalid inputs */
        return mask;
    }

    /* geodetic location */
    lambda = atan2(y, x);
    gpsd_sincos(lambda, &sin_lambda, &cos_lambda);

    p = sqrt(pow(x, 2) + pow(y, 2));
    theta = atan2(z * WGS84A, p * WGS84B);
    gpsd_sincos(theta, &sin_theta, &cos_theta);

    phi = atan2(z + WGS84E2 * WGS84B * pow(sin_theta, 3),
                p - WGS84E * WGS84A * pow(cos_theta, 3));
    gpsd_sincos(phi, &sin_phi, &cos_phi);

    n = WGS84A / sqrt(1.0 - WGS84E * pow(sin_phi, 2));

    /* altitude is WGS84 */
    fix->altHAE = (p / cos_phi) - n;

    fix->latitude = phi * RAD_2_DEG;
    fix->longitude = lambda * RAD_2_DEG;
    mask |= LATLON_SET | ALTITUDE_SET;

    /* velocity computation */
    vnorth = -vx * sin_phi * cos_lambda - vy * sin_phi * sin_lambda +
             vz * cos_phi;
    veast = -vx * sin_lambda + vy * cos_lambda;

    vup = vx * cos_phi * cos_lambda + vy * cos_phi * sin_lambda + vz * sin_phi;

    /* save velNED */
    fix->NED.velN = vnorth;
    fix->NED.velE = veast;
    fix->NED.velD = -vup;
    mask |= VNED_SET;

    /* velNED is saved, let gpsd_error_model() do the
     * sanity checks and calculate climb/speed/track */

    return mask;
}

#ifdef __UNUSED
/*
 * Some systems propagate the sign along with zero. This messes up
 * certain trig functions, like atan2():
 *    atan2(+0, +0) = 0
 *    atan2(+0, -0) = PI
 * Obviously that will break things. Luckily the "==" operator thinks
 * that -0 == +0; we will use this to return an unambiguous value.
 *
 * I hereby decree that zero is not allowed to have a negative sign!
 */
static double fix_minuz(double d)
{
    return ((d == 0.0) ? 0.0 : d);
}

/* atan2() protected by fix_minuz() */
static double atan2z(double y, double x)
{
    return atan2(fix_minuz(y), fix_minuz(x));
}
#endif /* __UNUSED */
// vim: set expandtab shiftwidth=4
